import random
import socket
from typing import Optional


def get_free_port_in_allow_set(allow_set: set[int]) -> Optional[int]:
    """
    Finds an available port within the specified range [min_port, max_port].

    :param allow_set: A set of ports that are allowed to allocate.
    :return: An available port number or None if no port is available.
    """
    for port in allow_set:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            try:
                s.bind(("", port))
                return port
            except OSError:
                continue
    return None


def get_free_port(min_port: int, max_port: int, block_set: set[int]) -> Optional[int]:
    """
    Finds an available port within the specified range [min_port, max_port].

    :param min_port: The minimum port number in the range.
    :param max_port: The maximum port number in the range.
    :param temp_allocated_ports: A set of ports that should not allocate.
    :return: An available port number or None if no port is available.
    """
    for port in range(
        min_port + random.randint(0, int((max_port - min_port) * 0.5)), max_port + 1
    ):
        if port not in block_set:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                try:
                    s.bind(("", port))
                    return port
                except OSError:
                    continue
    return None


def is_valid_docker_container_name(name: str) -> bool:
    """
    判断字符串是否为有效的 Docker 容器名称。

    :param name: 待验证的容器名称字符串
    :return: 如果是有效的容器名称返回 True，否则返回 False
    """
    import re

    # 定义正则表达式，匹配有效的 Docker 容器名称
    pattern = r"^[a-z0-9]([a-z0-9_.-]*[a-z0-9])?$"

    # 检查名称长度是否在 1 到 128 个字符之间
    if not 1 <= len(name) <= 128:
        return False

    # 使用正则表达式验证名称格式
    if re.match(pattern, name):
        return True

    return False


def size_bytes_to_human_readable(size_byte: int) -> str:
    """
    Convert size in bytes to human readable format.
    """
    if size_byte < 1024:
        return f"{size_byte} B"
    elif size_byte < 1024 * 1024:
        return f"{size_byte / 1024:.2f} KB"
    elif size_byte < 1024 * 1024 * 1024:
        return f"{size_byte / (1024 * 1024):.2f} MB"
    else:
        return f"{size_byte / (1024 * 1024 * 1024):.2f} GB"


def join_url(base_url: str, path: str) -> str:
    return f"{base_url.rstrip('/')}/{path.lstrip('/')}"
